package com.linkedin.databus.bootstrap.utils;
/*
 *
 * Copyright 2013 LinkedIn Corp. All rights reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/


import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;

import com.linkedin.databus.bootstrap.api.BootstrapProducerStatus;
import com.linkedin.databus.bootstrap.common.BootstrapConfig;
import com.linkedin.databus.bootstrap.common.BootstrapConn;
import com.linkedin.databus.bootstrap.common.BootstrapDBMetaDataDAO;
import com.linkedin.databus.bootstrap.common.BootstrapReadOnlyConfig;
import com.linkedin.databus.core.util.ConfigLoader;

public class BootstrapAddSource
{
	public static final String MODULE = BootstrapDropSource.class.getName();
	public static final Logger LOG = Logger.getLogger(MODULE);

	public static final String HELP_OPT_LONG_NAME = "help";
	public static final char   HELP_OPT_CHAR = 'h';
	public static final String BOOTSTRAP_DB_PROPS_OPT_LONG_NAME = "bootstrap_db_props";
	public static final char   BOOTSTRAP_DB_PROP_OPT_CHAR = 'p';
	public static final String CMD_LINE_PROPS_OPT_LONG_NAME = "cmdline_props";
	public static final char CMD_LINE_PROPS_OPT_CHAR = 'c';
	public static final String LOG4J_PROPS_OPT_LONG_NAME = "log_props";
	public static final char LOG4J_PROPS_OPT_CHAR = 'l';
	public static final String SOURCE_NAME_OPT_LONG_NAME = "source_name";
	public static final char   SOURCE_NAME_OPT_CHAR = 's';
	public static final String SOURCE_ID_OPT_LONG_NAME = "source_id";
	public static final char   SOURCE_ID_OPT_CHAR = 'i';

	private static Properties  _sBootstrapConfigProps = null;
	private int           _srcId               = 0;
	private String        _srcName;

	private BootstrapDBMetaDataDAO           _dbDao       = null;
	private BootstrapReadOnlyConfig _config              = null;

	public BootstrapAddSource(BootstrapReadOnlyConfig config, String srcName, int srcId)
	{
		_srcId = srcId;
		_srcName = srcName;
		_config = config;
	}
	/*
	 * @return a bootstrapDB connection object.
	 * Note: The connection object is still owned by BootstrapConn. SO dont close it
	 */
	public Connection getConnection()
	{
		Connection conn = null;

		if (_dbDao == null)
		{
			LOG.info("<<<< Creating Bootstrap Connection!! >>>>");
			BootstrapConn dbConn = new BootstrapConn();
			try
			{
				dbConn.initBootstrapConn(false,
						_config.getBootstrapDBUsername(),
						_config.getBootstrapDBPassword(),
						_config.getBootstrapDBHostname(),
						_config.getBootstrapDBName());
				_dbDao = new BootstrapDBMetaDataDAO(dbConn,
						_config.getBootstrapDBHostname(),
						_config.getBootstrapDBUsername(),
						_config.getBootstrapDBPassword(),
						_config.getBootstrapDBName(),
						false);
			} catch (Exception e) {
				LOG.fatal("Unable to open BootstrapDB Connection !!", e);
				throw new RuntimeException("Got exception when getting bootstrap DB Connection.",e);
			}
		}

		try
		{
			conn = _dbDao.getBootstrapConn().getDBConn();
		} catch ( SQLException e) {
			LOG.fatal("Not able to open BootstrapDB Connection !!", e);
			throw new RuntimeException("Got exception when getting bootstrap DB Connection.",e);
		}
		return conn;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args)
			throws Exception
	{
		Config cfg = parseArgs(args);
		BootstrapAddSource adder = new BootstrapAddSource(cfg.getDbConfig(), cfg.getSrcName(), cfg.getSrcId());
		adder.add();
	}


	public void add()
			throws Exception
	{
		getConnection();

		_dbDao.addNewSourceInDB(_srcId, _srcName, BootstrapProducerStatus.NEW);
	}

	@SuppressWarnings("static-access")
	public static Config parseArgs(String[] args) throws Exception
	{
		CommandLineParser cliParser = new GnuParser();


		Option helpOption = OptionBuilder.withLongOpt(HELP_OPT_LONG_NAME)
				.withDescription("Help screen")
				.create(HELP_OPT_CHAR);

		Option sourceIdOption = OptionBuilder.withLongOpt(SOURCE_ID_OPT_LONG_NAME)
				.withDescription("Source ID for which tables need to be added")
				.hasArg()
				.withArgName("Source ID")
				.create(SOURCE_ID_OPT_CHAR);

		Option sourceNameOption = OptionBuilder.withLongOpt(SOURCE_NAME_OPT_LONG_NAME)
				.withDescription("Source Name for which tables need to be added")
				.hasArg()
				.withArgName("Source ID")
				.create(SOURCE_NAME_OPT_CHAR);

		Option dbOption = OptionBuilder.withLongOpt(BOOTSTRAP_DB_PROPS_OPT_LONG_NAME)
				.withDescription("Bootstrap producer properties to use")
				.hasArg()
				.withArgName("property_file")
				.create(BOOTSTRAP_DB_PROP_OPT_CHAR);

		Option cmdLinePropsOption = OptionBuilder.withLongOpt(CMD_LINE_PROPS_OPT_LONG_NAME)
				.withDescription("Cmd line override of config properties. Semicolon separated.")
				.hasArg()
				.withArgName("Semicolon_separated_properties")
				.create(CMD_LINE_PROPS_OPT_CHAR);

		Option log4jPropsOption = OptionBuilder.withLongOpt(LOG4J_PROPS_OPT_LONG_NAME)
				.withDescription("Log4j properties to use")
				.hasArg()
				.withArgName("property_file")
				.create(LOG4J_PROPS_OPT_CHAR);

		Options options = new Options();
		options.addOption(helpOption);
		options.addOption(sourceIdOption);
		options.addOption(sourceNameOption);
		options.addOption(dbOption);
		options.addOption(cmdLinePropsOption);
		options.addOption(log4jPropsOption);

		CommandLine cmd = null;
		try
		{
			cmd = cliParser.parse(options, args);
		}
		catch (ParseException pe)
		{
			LOG.error("Bootstrap Physical Config: failed to parse command-line options.", pe);
			throw new RuntimeException("Bootstrap Physical Config: failed to parse command-line options.", pe);
		}

		if (cmd.hasOption(LOG4J_PROPS_OPT_CHAR))
		{
			String log4jPropFile = cmd.getOptionValue(LOG4J_PROPS_OPT_CHAR);
			PropertyConfigurator.configure(log4jPropFile);
			LOG.info("Using custom logging settings from file " + log4jPropFile);
		}
		else
		{
			PatternLayout defaultLayout = new PatternLayout("%d{ISO8601} +%r [%t] (%p) {%c} %m%n");
			ConsoleAppender defaultAppender = new ConsoleAppender(defaultLayout);

			Logger.getRootLogger().removeAllAppenders();
			Logger.getRootLogger().addAppender(defaultAppender);

			LOG.info("Using default logging settings");
		}

		if (cmd.hasOption(HELP_OPT_CHAR))
		{
			printCliHelp(options);
			System.exit(0);
		}

		if ( !cmd.hasOption(SOURCE_ID_OPT_CHAR))
			throw new RuntimeException("Source ID is not provided");

		if ( !cmd.hasOption(SOURCE_NAME_OPT_CHAR))
			throw new RuntimeException("Source Name is not provided");

		if (!cmd.hasOption(BOOTSTRAP_DB_PROP_OPT_CHAR) )
			throw new RuntimeException("Bootstrap config is not provided");

		String propFile = cmd.getOptionValue(BOOTSTRAP_DB_PROP_OPT_CHAR);
		LOG.info("Loading bootstrap DB config from properties file " + propFile);

		_sBootstrapConfigProps = new Properties();
		FileInputStream f = new FileInputStream(propFile);
		try
		{
			_sBootstrapConfigProps.load(f);
		} finally {
			f.close();
		}
		if (cmd.hasOption(CMD_LINE_PROPS_OPT_CHAR))
		{
			String cmdLinePropString = cmd.getOptionValue(CMD_LINE_PROPS_OPT_CHAR);
			updatePropsFromCmdLine(_sBootstrapConfigProps, cmdLinePropString);
		}

		int srcId = Integer.parseInt(cmd.getOptionValue(SOURCE_ID_OPT_CHAR));
		String srcName = cmd.getOptionValue(SOURCE_NAME_OPT_CHAR);

		BootstrapConfig config = new BootstrapConfig();

		ConfigLoader<BootstrapReadOnlyConfig> configLoader =
				new ConfigLoader<BootstrapReadOnlyConfig>("bootstrap.", config);

		BootstrapReadOnlyConfig staticConfig = configLoader.loadConfig(_sBootstrapConfigProps);
		Config cfg = new Config();
		cfg.setDbConfig(staticConfig);
		cfg.setSrcId(srcId);
		cfg.setSrcName(srcName);
		return cfg;
	}

	private static void updatePropsFromCmdLine(Properties props, String cmdLinePropString)
	{
		String[] cmdLinePropSplit = cmdLinePropString.split(";");
		for(String s : cmdLinePropSplit)
		{
			String[] onePropSplit = s.split("=");
			if (onePropSplit.length != 2)
			{
				LOG.error("CMD line property setting " + s + "is not valid!");
			}
			else
			{
				LOG.info("CMD line Property overwride: " + s);
				props.put(onePropSplit[0], onePropSplit[1]);
			}
		}
	}

	private static void printCliHelp(Options cliOptions)
	{
		HelpFormatter helpFormatter = new HelpFormatter();
		helpFormatter.printHelp("java " + BootstrapAddSource.class.getName(), cliOptions);
	}


	public static class Config
	{
		private String srcName;
		private Integer srcId;
		private BootstrapReadOnlyConfig dbConfig;
		public String getSrcName() {
			return srcName;
		}
		public void setSrcName(String srcName) {
			this.srcName = srcName;
		}
		public Integer getSrcId() {
			return srcId;
		}
		public void setSrcId(Integer srcId) {
			this.srcId = srcId;
		}
		public BootstrapReadOnlyConfig getDbConfig() {
			return dbConfig;
		}
		public void setDbConfig(BootstrapReadOnlyConfig dbConfig) {
			this.dbConfig = dbConfig;
		}


	}
}
